#ifndef PUDGE_META_DUNION_HPP
#define PUDGE_META_DUNION_HPP

#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>

/// A simple discriminating union. 
/// Dunion conforms to a standard visitor/client interface.
///
/// template<class Visitor> void operator() (Visitor & visitor) 
///	{ 
///		visitor( client_derived ); 
///	}
///
/// struct my_visitor{ void operator ()(double & d) { d = 7.5; } };
///
/// dunion<int,double> d;
/// d = 1.0;
/// d(my_visitor());
///
/// d now hase a value of 7.5
///
/// attempting to visit an unassigned dunion will result in a runtime error being thrown

namespace pudge { namespace meta {

#ifndef PUDGE_META_DUNION_PARAMS
#define PUDGE_META_DUNION_PARAMS 9
#endif

	struct null_dunion_type {};

#define PUDGE_META_DUNION_M(Z,N,A) class T ## N = null_dunion_type
	template<BOOST_PP_ENUM(PUDGE_META_DUNION_PARAMS, PUDGE_META_DUNION_M,)>
#undef PUDGE_META_DUNION_M

	struct dunion
	{
		typedef dunion<BOOST_PP_ENUM_PARAMS(PUDGE_META_DUNION_PARAMS,T)> self_type;
		enum
		{
#define PUDGE_META_DUNION_M(Z,N,A) T ## N ## _TYPE
			BOOST_PP_ENUM(PUDGE_META_DUNION_PARAMS,PUDGE_META_DUNION_M,)
#undef PUDGE_META_DUNION_M
			, NULL_TYPE
		} type;
		union
		{
#define PUDGE_META_DUNION_M(Z,N,A) T ## N t ## N;
			BOOST_PP_REPEAT(PUDGE_META_DUNION_PARAMS,PUDGE_META_DUNION_M,)
#undef PUDGE_META_DUNION_M
		};
		template<class F> void operator()(F & f)
		{
			switch(type)
			{
#define PUDGE_META_DUNION_M(Z,N,A) case T ## N ## _TYPE: f(t ## N); break;
				BOOST_PP_REPEAT(PUDGE_META_DUNION_PARAMS,PUDGE_META_DUNION_M,)
#undef PUDGE_META_DUNION_M

case NULL_TYPE:
	throw -1;
	break;
			};
		}
#define PUDGE_META_DUNION_M(Z,N,A) \
	self_type & operator = (T ## N t ## N ##_) \
		{ \
		t ## N = t ## N ## _; \
		type = T ## N ## _TYPE; \
		return *this; \
		}
		BOOST_PP_REPEAT(PUDGE_META_DUNION_PARAMS,PUDGE_META_DUNION_M,)
#undef PUDGE_META_DUNION_M
	};

#define BOOST_PP_FILENAME_1 "pudge/meta/detail/unroll_dunion.hpp"
#define BOOST_PP_ITERATION_LIMITS (1,BOOST_PP_DEC(PUDGE_META_DUNION_PARAMS))
#include BOOST_PP_ITERATE()
}}

#endif